package com.jiangli.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jiangli.entity.*;
import com.jiangli.mapper.DishFlavorMapper;
import com.jiangli.mapper.DishMapper;
import com.jiangli.mapper.DishRatingMapper;
import com.jiangli.mapper.IngredientMapper;
import com.jiangli.result.PageResult;
import com.jiangli.service.DishService;
import com.jiangli.service.UserPreferenceService;
import com.jiangli.service.UserService;
import com.jiangli.utils.HashObjectConverter;
import com.jiangli.utils.RedisLock;
import com.jiangli.utils.SessionRandomUtil;
import com.jiangli.vo.DishBriefVO;
import com.jiangli.vo.DishRatingVO;
import com.jiangli.vo.DishVO;
import com.jiangli.vo.UserLoginVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Proxy;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;
    @Autowired
    private UserPreferenceService preferenceService;
    @Autowired
    private MerchantServiceImpl merchantService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedisLock redisLock;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private IngredientMapper ingredientMapper;
    @Autowired
    private DishRatingMapper dishRatingMapper;
    @Autowired
    private UserService userService;
    private final Random random = new Random();

    // Redis键前缀
    private static final String RECOMMEND_PREFIX = "dish:recommend:";
    private static final String DISH_DETAIL_PREFIX = "dish:detail:";
    private static final String DISH_METADATA_KEY = "dish:all:metadata";
    private static final String TOTAL_COUNT_KEY = "dish:total:count";
    private static final String CATEGORY_IDS_KEY_PREFIX = "dish:category:ids:";
    private static final String DISH_LOCK_KEY = "dish:lock:metadata";
    private static final String DISH_BATCH_LOCK_PREFIX = "dish:batch:lock:";
    private static final String DISH_INGREDIENT_LOCK_PREFIX = "dish:ingredient:lock:";
    private static final String DISH_INGREDIENT_DETAIL_PREFIX = "ingredient:detail:";
    private static final String DISH_INGREDIENT_PREFIX = "dish:ingredientIds:";
    private static final String INGREDIENT_DETAIL_LOCK_PREFIX = "ingredient:lock:";
    private static final long NORMAL_CACHE_TTL_SECONDS = 86400;
    private static final long INGREDIENT_CACHE_TTL_SECONDS = 3600;
    private static final String DISH_RANK_METADATA = "dish:rank:metadata";
    private static final String DISH_CALORIE_RANK_METADATA = "dish:calorieRank:metadata";
    private static final String DISH_RATING_KEY_PREFIX = "dish:rating:key:";
    private final ConcurrentHashMap<Integer, Lock> categoryLocks = new ConcurrentHashMap<>();
    private static final double RANDOM_RATIO = 0.50;
    @Autowired
    private DishFlavorMapper dishFlavorMapper;


    @Override
    public PageResult getDishList(Integer page, Integer pageSize) {
        if (page == null || page < 1) {
            page = 1;
        }
        if (pageSize == null || pageSize < 1 || pageSize > 100) {
            pageSize = 10;
        }
        if(page != 1) {
            SessionRandomUtil.clearRandomIds();
        }

        List<Long> randomIds = SessionRandomUtil.getRandomIds();
        if (randomIds == null || randomIds.isEmpty()) {
            randomIds = dishMapper.selectAllIds();
            SessionRandomUtil.setRandomIds(randomIds);
        }
        int offset = (page - 1) * pageSize;
        if (offset >= randomIds.size()) {
            return new PageResult(randomIds.size(), new ArrayList<>());
        }
        List<DishBriefVO> dishBriefVOList = dishResultByPage(randomIds,page,pageSize);
        return new PageResult(randomIds.size(), dishBriefVOList);
    }

    @Override
    public PageResult getRecommendedDishByPage(Long userId, Integer page, Integer pageSize, boolean reset) {
        if (page == null || page < 1) page = 1;
        if (pageSize == null || pageSize < 1 || pageSize > 100) pageSize = 10;

        String recommendKey = RECOMMEND_PREFIX + userId;
        if (reset) {
            redisTemplate.delete(recommendKey);
        }

        Long total = getTotalCount();
        if (total == 0) {
            return new PageResult(pageSize, Collections.emptyList());
        }
        List<Dish> dishList = getAllDishesWithCache();
        List<Long> recommendIds = getRecommendIdsFromRedis(recommendKey);
        if (recommendIds == null || recommendIds.isEmpty()) {
            log.info("用户推荐列表为空，重新生成，用户ID: {}", userId);
            recommendIds = generateRecommendIds(userId, dishList.size());
            saveRecommendIdsToRedis(recommendKey, recommendIds);
        }

//        int offset = (page - 1) * pageSize;
//        if (offset >= recommendIds.size()) {
//            List<Long> moreIds = generateRecommendIds(userId, pageSize * 5);
//            List<Long> newRecommendIds = new ArrayList<>(recommendIds);
//            newRecommendIds.addAll(moreIds);
//            recommendIds = newRecommendIds; // 更新引用
//            saveRecommendIdsToRedis(recommendKey, recommendIds);
//        }
        List<DishBriefVO> dishBriefVOList = dishResultByPage(recommendIds,page,pageSize);

        return new PageResult(total, dishBriefVOList);
    }

    @Override
    public DishBriefVO getDishRandom(Integer mealType, Integer count) {
        if (isInvalidParams(mealType, count)) {
            log.warn("获取随机菜品参数无效，mealType: {}, count: {}", mealType, count);
            return null;
        }

        List<String> randomIds = getRandomDishIds(mealType, count);
        if (randomIds.isEmpty()) {
            log.info("未获取到随机菜品，类型: {}", mealType);
            return null;
        }

        try {
            List<Long> randomIdList = randomIds.stream().map(Long::parseLong).collect(Collectors.toList());
            List<Dish> listDish = getDishesFromCacheOrDB(randomIdList);
            if (listDish.isEmpty()) {
                return null;
            }

            DishBriefVO dishVO = new DishBriefVO();
            BeanUtils.copyProperties(listDish.get(0), dishVO);
            List<Long> merchantIds = Collections.singletonList(Long.valueOf(listDish.get(0).getMerchantId()));
            List<Merchant> merchant = merchantService.getCurrentMerchant(merchantIds);
            dishVO.setMerchant(merchant.get(0).getMerchantName());
            dishVO.setLocation(merchant.get(0).getLocation());
            return dishVO;
        } catch (Exception e) {
            log.error("处理随机菜品失败，随机ID列表: {}", randomIds, e);
            return null;
        }
    }

    @Override
    public DishVO getDishDetailById(Long id) {
        try {
            List<Long> ids = Collections.singletonList(id);
            List<Dish> dishList = getDishesFromCacheOrDB(ids);
            if (dishList.isEmpty()) {
                log.info("未查询到菜品详情，ID: {}", id);
                return null;
            }

            DishVO dishVO = new DishVO();
            BeanUtils.copyProperties(dishList.get(0), dishVO);
            List<Ingredient> ingredient = getDishIngredient(id);
            List<Long> merchantIds = Collections.singletonList(Long.valueOf(dishList.get(0).getMerchantId()));
            List<Merchant> merchant = merchantService.getCurrentMerchant(merchantIds);
            DishFlavor dishFlavor = dishFlavorMapper.getFlavorByDishId(id);
            List<String> flavor = null;
            if(dishFlavor!=null && dishFlavor.getFlavorName()!=null)
            {flavor = objectMapper.readValue(dishFlavor.getFlavorName(),new TypeReference<ArrayList<String>>() {});}

            dishVO.setMerchantId(merchant.get(0).getId());
            dishVO.setMerchant(merchant.get(0).getMerchantName());
            dishVO.setIngredients(ingredient);
            dishVO.setLocation(merchant.get(0).getLocation());
            dishVO.setFlavor(flavor);
            return dishVO;
        } catch (Exception e) {
            log.error("获取菜品详情失败，菜品ID: {}", id, e);
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rateDish(Long userId, Long dishId, Double score) {
        try {
            if (score < 0.5 || score > 5) {
                log.warn("评分无效，用户ID: {}, 菜品ID: {}, 评分: {}", userId, dishId, score);
                throw new IllegalArgumentException("评分必须在1-5之间");
            }
            DishServiceImpl proxy = (DishServiceImpl) AopContext.currentProxy();
            DishRating existingRating = dishRatingMapper.selectByUserIdAndDishId(userId, dishId);
            redisTemplate.delete(DISH_METADATA_KEY);
            if (existingRating != null) {
                log.info("已经存在{}", existingRating);
                Double lastScore = existingRating.getScore();
                double cha = score - lastScore;

                proxy.updateRating(dishId, cha, false);
                updateRating(dishId, cha, false);
                existingRating.setScore(score);
                return dishRatingMapper.updateScore(existingRating) > 0;
            } else {
                log.info("新增加{}",score);
                proxy.updateRating(dishId, score, true);
                DishRating newRating = new DishRating();
                newRating.setUserId(userId);
                newRating.setDishId(dishId);
                newRating.setScore(score);
                return dishRatingMapper.insert(newRating) > 0;
            }
        } catch (Exception e) {
            log.error("用户评分失败，用户ID: {}, 菜品ID: {}, 评分: {}", userId, dishId, score, e);
            return false;
        }
    }

    @Override
    public PageResult getRank(Integer page, Integer pageSize, boolean reset) {
        try {
            if (pageSize == null || pageSize < 1 || pageSize > 100) pageSize = 10;
            if (page == null || page < 1 || page > 100) page = 1;
            if (reset) {

                redisTemplate.delete(DISH_RANK_METADATA);
                log.info("删除排行缓存成功");
            }

            List<Long> rankIds = getRankIds();
            PageResult pageResult = new PageResult();
            pageResult.setTotal(rankIds.size());
            List<DishBriefVO> dishVOList = dishResultByPage(rankIds, page, pageSize);
            pageResult.setRecords(dishVOList);
            return pageResult;
        } catch (Exception e) {
            log.error("获取菜品排行失败，页码: {}, 页大小: {}", page, pageSize, e);
            return new PageResult(0, Collections.emptyList());
        }
    }

    @Override
    public DishRatingVO getUserRating(Long userId, Long dishId) {
        try {
            DishRating dishRating = dishRatingMapper.selectByUserIdAndDishId(userId, dishId);
            if (dishRating == null) {
                return null;
            }
            log.info("score{}",dishRating.getScore());
            DishRatingVO dishRatingVO = new DishRatingVO();
            BeanUtils.copyProperties(dishRating, dishRatingVO);
            return dishRatingVO;
        } catch (Exception e) {
            log.error("获取用户评分失败，用户ID: {}, 菜品ID: {}", userId, dishId, e);
            return null;
        }
    }

    @Override
    public PageResult getCalorieRank(Integer page, Integer pageSize, boolean reset) {
        try {
            List<Long> dishIds = getCalorieRankIds();
            if (reset) {
                redisTemplate.delete(DISH_CALORIE_RANK_METADATA);
            }
            List<DishBriefVO> dishVOList = dishResultByPage(dishIds, page, pageSize);
            PageResult pageResult = new PageResult();
            pageResult.setTotal(dishIds.size());
            pageResult.setRecords(dishVOList);
            return pageResult;
        } catch (Exception e) {
            log.error("获取热量排行失败，页码: {}, 页大小: {}", page, pageSize, e);
            return new PageResult(0, Collections.emptyList());
        }
    }

    private List<Long> getCalorieRankIds() {
        try {
            String strJson = redisTemplate.opsForValue().get(DISH_CALORIE_RANK_METADATA);
            if (strJson != null) {
                return objectMapper.readValue(strJson, new TypeReference<List<Long>>() {});
            }

            List<Dish> dishList = getAllDishesWithCache();
            List<Long> randDishIds = dishList.stream()
                    .sorted((o1, o2) -> o1.getCalorie() > o2.getCalorie() ? -1 : 1)
                    .map(Dish::getId)
                    .collect(Collectors.toList());
            int end = Math.min(100, randDishIds.size());
            randDishIds = randDishIds.subList(0, end);

            redisTemplate.opsForValue().set(DISH_CALORIE_RANK_METADATA, objectMapper.writeValueAsString(randDishIds), 6, TimeUnit.HOURS);
            return randDishIds;
        } catch (Exception e) {
            log.error("获取热量排行ID失败", e);
            return Collections.emptyList();
        }
    }

    List<DishBriefVO> dishResultByPage(List<Long> dishIds, int page, int pageSize) {
        if (dishIds == null || dishIds.isEmpty()) {
            return Collections.emptyList();
        }

        int total = dishIds.size();
        int offset = (page - 1) * pageSize;
        if (offset >= total) {
            return Collections.emptyList();
        }

        int end = Math.min(offset + pageSize, total);
        List<Long> currentPageIds = new ArrayList<>(dishIds.subList(offset, end));
        List<Dish> dishList = getDishesFromCacheOrDB(currentPageIds);
        List<DishBriefVO> dishVOList = new ArrayList<>(dishList.size());

        for (Dish dish : dishList) {
            DishBriefVO dishVO = new DishBriefVO();
            BeanUtils.copyProperties(dish, dishVO);
            try {
                Long merchantId = Long.valueOf(dish.getMerchantId());
                List<Merchant> list = merchantService.getCurrentMerchant(Collections.singletonList(merchantId));
                Merchant merchant = list.isEmpty() ? new Merchant() : list.get(0);
                dishVO.setMerchant(merchant.getMerchantName());
                dishVO.setLocation(merchant.getLocation());
            } catch (Exception e) {
                log.error("转换排行菜品商家信息失败，菜品ID: {}", dish.getId(), e);
            }
            dishVOList.add(dishVO);
        }
        return dishVOList;
    }

    private List<Long> getRankIds() {
        try {
            String strJson = redisTemplate.opsForValue().get(DISH_RANK_METADATA);
            if (strJson != null) {
                return objectMapper.readValue(strJson, new TypeReference<List<Long>>() {});
            }

            List<Dish> dishList = getAllDishesWithCache();
            log.info("dishList的大小{}",dishList.size());
            List<Long> randDishIds = dishList.stream()
                    .sorted((o1, o2) -> {
                        // 1. 按评分降序排序（高的在前）
                        float rating1 = o1.getDishRating();
                        float rating2 = o2.getDishRating();
                        // 浮点数降序：先比较rating2和rating1，正数表示o2>o1，应让o2排在前面
                        int ratingCompare = Float.compare(rating2, rating1);
                        if (ratingCompare != 0) {
                            return ratingCompare;
                        }

                        // 2. 评分相同，按评分人数降序（多的在前）
                        long count1 = o1.getRatingCount();
                        long count2 = o2.getRatingCount();
                        return Long.compare(count2, count1);
                    })
                    .map(Dish::getId)
                    .collect(Collectors.toList());
            int end = Math.min(100, randDishIds.size());
            log.info("{}",randDishIds.size());
            randDishIds = randDishIds.subList(0, end);
            redisTemplate.opsForValue().set(DISH_RANK_METADATA, objectMapper.writeValueAsString(randDishIds), 12, TimeUnit.HOURS);
            return randDishIds;
        } catch (Exception e) {
            log.error("获取菜品排行ID失败", e);
            return Collections.emptyList();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateRating(Long id, Double rating, boolean add) {
        try {
            redisLock.executeWithLock(DISH_RATING_KEY_PREFIX + id, new Supplier<Void>() {
                @Override
                public Void get() {
                    List<Long> ids = Collections.singletonList(id);
                    List<Dish> dishList = getDishesFromCacheOrDB(ids);
                    if (dishList.isEmpty()) {
                        log.warn("更新评分失败，菜品不存在，ID: {}", id);
                        return null;
                    }

                    Dish dish = dishList.get(0);
                    float currentRating = dish.getDishRating();
                    Long count = dish.getRatingCount();
                    double totalRating = currentRating * count + rating;

                    if (add) {
                        count++;
                    }
                    if(totalRating == 0)
                    {
                        currentRating = 0;
                    }else
                    {currentRating = (float) (totalRating / count);}
                    dish.setDishRating(currentRating);
                    dish.setRatingCount(count);
                    if(updateDish(dish))
                    {
                        log.info("更新成功");
                    }
                    return null;
                }
            });

        } catch (Exception e) {
            log.error("更新菜品评分失败，菜品ID: {}", id, e);
            throw new RuntimeException("更新评分失败", e);
        }
    }

    public boolean updateDish(Dish dish) {
        try {
            dishMapper.update(dish);
            redisTemplate.delete(DISH_DETAIL_PREFIX + dish.getId());
            return true;
        } catch (Exception e) {
            log.error("更新菜品信息失败，菜品ID: {}", dish.getId(), e);
            return false;
        }
    }

    private List<Ingredient> getDishIngredient(Long id) {
        String key = DISH_INGREDIENT_PREFIX + id;
        String emptyKey = key + ":empty";

        // 1. 先检查空值标记（防穿透核心：直接拦截不存在的数据请求）
        Boolean isEmpty = redisTemplate.hasKey(emptyKey);
        if (Boolean.TRUE.equals(isEmpty)) {
            log.debug("命中空值缓存，菜品ID: {}", id);
            return Collections.emptyList(); // 直接返回空，不查库
        }

        // 2. 尝试从主缓存获取（List类型）
        List<String> ingredientIds = redisTemplate.opsForList().range(key, 0, -1);
        boolean keyExists = Boolean.TRUE.equals(redisTemplate.hasKey(key));

        // 3. 主缓存未命中，加锁查询数据库（防止缓存击穿+穿透）
        if (!keyExists || CollectionUtils.isEmpty(ingredientIds)) {
            ingredientIds = redisLock.executeWithLock(
                    DISH_INGREDIENT_LOCK_PREFIX + id,
                    () -> {
                        // 双重检查：防止锁等待期间缓存已被其他线程更新
                        if (Boolean.TRUE.equals(redisTemplate.hasKey(emptyKey))) {
                            return Collections.emptyList();
                        }
                        List<String> cachedIds = redisTemplate.opsForList().range(key, 0, -1);
                        if (Boolean.TRUE.equals(redisTemplate.hasKey(key)) && !CollectionUtils.isEmpty(cachedIds)) {
                            return cachedIds;
                        }

                        // 4. 穿透到数据库查询（这一步应该极少执行）
                        log.warn("缓存未命中，查询数据库，菜品ID: {}", id);
                        List<String> dbIds = ingredientMapper.getIngredientIdWithDishId(Math.toIntExact(id));

                        // 5. 数据库查询结果处理（关键防穿透逻辑）
                        if (CollectionUtils.isEmpty(dbIds)) {
                            // 数据库也无数据：设置空值标记，有效期较短（如5分钟）
                            redisTemplate.opsForValue().set(emptyKey, "1", 300, TimeUnit.SECONDS);
                            log.debug("设置空值缓存，菜品ID: {}", id);
                            return Collections.emptyList();
                        } else {
                            // 数据库有数据：正常缓存List，有效期较长（如1小时）
                            redisTemplate.opsForList().rightPushAll(key, dbIds);
                            redisTemplate.expire(key, 3600, TimeUnit.SECONDS);
                            return dbIds;
                        }
                    }
            );
        }

        // 6. 转换ID并返回详情
        if (!CollectionUtils.isEmpty(ingredientIds)) {
            try {
                List<Long> ids = ingredientIds.stream().map(Long::valueOf).collect(Collectors.toList());
                return getDetailIngredient(ids);
            } catch (NumberFormatException e) {
                log.error("食材ID转换失败，ID列表: {}", ingredientIds, e);
            }
        }
        return Collections.emptyList();
    }

    public List<Ingredient> getDetailIngredient(List<Long> ingredientIds) {
        if (ingredientIds == null || ingredientIds.isEmpty()) {
            return Collections.emptyList();
        }

        List<Ingredient> ingredients = new ArrayList<>();
        List<Long> isExist = new ArrayList<>();

        for (Long id : ingredientIds) {
            if (Boolean.TRUE.equals(redisTemplate.hasKey(DISH_INGREDIENT_DETAIL_PREFIX + id + ":empty"))) {
                continue;
            }

            Ingredient ingredient = HashObjectConverter.hashToObject(redisTemplate, DISH_INGREDIENT_DETAIL_PREFIX + id, Ingredient.class);
            if (ingredient == null) {
                isExist.add(id);
            } else {
                ingredients.add(ingredient);
            }
        }

        if (!isExist.isEmpty()) {
            for (Long id : isExist) {
                try {
                    Ingredient ingredientA = redisLock.executeWithLock(INGREDIENT_DETAIL_LOCK_PREFIX + id, () -> {
                        if (Boolean.TRUE.equals(redisTemplate.hasKey(DISH_INGREDIENT_DETAIL_PREFIX + id + ":empty"))) {
                            return null;
                        }
                        Ingredient ingredient = HashObjectConverter.hashToObject(redisTemplate, DISH_INGREDIENT_DETAIL_PREFIX + id, Ingredient.class);
                        if (ingredient != null) {
                            return ingredient;
                        }

                        ingredient = ingredientMapper.getIngredientWithId(id);
                        if (ingredient != null) {
                            HashObjectConverter.objectToHash(redisTemplate, DISH_INGREDIENT_DETAIL_PREFIX + id, ingredient, INGREDIENT_CACHE_TTL_SECONDS);
                        } else {
                            redisTemplate.opsForValue().set(DISH_INGREDIENT_DETAIL_PREFIX + id + ":empty", "", 60, TimeUnit.SECONDS);
                        }
                        return ingredient;
                    });
                    if (ingredientA != null) {
                        ingredients.add(ingredientA);
                    }
                } catch (Exception e) {
                    log.error("获取食材详情失败，食材ID: {}", id, e);
                }
            }
        }
        return ingredients;
    }

    private List<Long> generateRecommendIds(Long userId, int count) {
        try {
            UserPreference preference = preferenceService.getPreference(userId);
            Map<String, Double> categoryWeights;
            if (preference != null && preference.getCategoryWeights() != null) {
                categoryWeights = objectMapper.readValue(preference.getCategoryWeights(), new TypeReference<HashMap<String, Double>>() {});
            } else {
                categoryWeights = Map.of();
            }

            List<Dish> allDishes = getAllDishesWithCache();
            if (allDishes.isEmpty()) {
                return Collections.emptyList();
            }
            UserLoginVO user = userService.getUserInfo(userId);List<String> userPrefers;
            if(user != null){
                userPrefers = user.getUserPrefers();
            } else {
                userPrefers = null;
            }

            List<Dish> sortedByPreference = allDishes.stream()
                    .sorted((d1, d2) -> {

                        double w1 = categoryWeights.getOrDefault(d1.getId().toString(), 0.5);
                        double w2 = categoryWeights.getOrDefault(d2.getId().toString(), 0.5);
                        DishFlavor dishFlavor1 = dishFlavorMapper.getFlavorByDishId(d1.getId());
                        DishFlavor dishFlavor2 = dishFlavorMapper.getFlavorByDishId(d2.getId());
                        if(dishFlavor1 != null && dishFlavor2 != null && userPrefers != null &&dishFlavor1.getFlavorName()!=null && dishFlavor2.getFlavorName()!=null) {
                            try {
                                for(String flavor : objectMapper.readValue(dishFlavor1.getFlavorName(), new TypeReference<List<String>>() {
                                })) {
                                    if(userPrefers.contains(flavor))
                                    {
                                        w1+=0.3;
                                    }
                                }
                                for(String flavor : objectMapper.readValue(dishFlavor2.getFlavorName(), new TypeReference<List<String>>(){})) {
                                    if(userPrefers.contains(flavor))
                                    {
                                        w2+=0.3;
                                    }}
                            } catch (JsonProcessingException e) {
                                throw new RuntimeException(e);
                            }

                        }
                        return Double.compare(w2, w1);
                    })
                    .toList();

            List<Dish> finalRecommend = new ArrayList<>();
            Set<Long> addedIds = new HashSet<>();

            for (Dish dish : sortedByPreference) {
                if (finalRecommend.size() >= count) {
                    break;
                }

                if (random.nextDouble() < RANDOM_RATIO) {
                    Dish randomDish = getRandomUniqueDishNotInCategory(allDishes, dish.getId().toString(), addedIds);
                    if (randomDish != null) {
                        finalRecommend.add(randomDish);
                        addedIds.add(randomDish.getId());
                        continue;
                    }
                }

                if (!addedIds.contains(dish.getId())) {
                    finalRecommend.add(dish);
                    addedIds.add(dish.getId());
                }
            }

            if (finalRecommend.size() < count) {
                for (Dish dish : allDishes) {
                    if (!addedIds.contains(dish.getId())) {
                        finalRecommend.add(dish);
                        addedIds.add(dish.getId());
                        if (finalRecommend.size() >= count) {
                            break;
                        }
                    }
                }
            }

            return finalRecommend.stream().map(Dish::getId).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("生成推荐菜品列表失败，用户ID: {}, 数量: {}", userId, count, e);
            return Collections.emptyList();
        }
    }

    public List<Dish> getAllDishesWithCache() {
        try {
            String json = redisTemplate.opsForValue().get(DISH_METADATA_KEY);
            if ("EMPTY".equals(json)) {
                return Collections.emptyList();
            }

            if (json != null) {
                List<DishMetaData> metaList = objectMapper.readValue(json, new TypeReference<List<DishMetaData>>() {});
                return metaList.stream().map(this::convertToDish).collect(Collectors.toList());
            }

            List<DishMetaData> metaDataList = redisLock.executeWithLock(DISH_LOCK_KEY, () -> {
                String jsonAgain = redisTemplate.opsForValue().get(DISH_METADATA_KEY);
                if (jsonAgain != null) {
                    if ("EMPTY".equals(jsonAgain)) {
                        return Collections.emptyList();
                    }
                    try {
                        return objectMapper.readValue(jsonAgain, new TypeReference<List<DishMetaData>>() {});
                    } catch (JsonProcessingException e) {
                        redisTemplate.delete(DISH_METADATA_KEY);
                    }
                }

                List<DishMetaData> dishesFromDb = dishMapper.selectAllMetadata();
                if (dishesFromDb == null || dishesFromDb.isEmpty()) {
                    redisTemplate.opsForValue().set(DISH_METADATA_KEY, "EMPTY", 1, TimeUnit.MINUTES);
                    return Collections.emptyList();
                }

                try {
                    redisTemplate.opsForValue().set(DISH_METADATA_KEY, objectMapper.writeValueAsString(dishesFromDb), 30, TimeUnit.MINUTES);
                } catch (JsonProcessingException e) {
                    log.error("缓存失败",e);
                    throw new RuntimeException(e);

                }
                return dishesFromDb;
            });

            if (metaDataList == null) {
                return Collections.emptyList();
            }
            return metaDataList.stream().map(this::convertToDish).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("获取菜品元数据失败", e);
            return Collections.emptyList();
        }
    }

    private Dish convertToDish(DishMetaData metaData) {
        if (metaData == null) {
            return null;
        }
        Dish dish = new Dish();
        dish.setId(metaData.getId());
        dish.setDishRating(metaData.getDishRating());
        dish.setDishName(metaData.getDishName());
        dish.setCalorie(metaData.getCalorie());
        dish.setDishCategoryId(metaData.getDishCategoryId());
        dish.setMerchantId(Math.toIntExact(metaData.getMerchantId()));
        dish.setRatingCount(metaData.getRatingCount());
        return dish;
    }

    public List<Dish> getDishesFromCacheOrDB(List<Long> ids) {
        if (ids.isEmpty()) {
            return Collections.emptyList();
        }
        Map<Long, Dish> dishMap = new HashMap<>(ids.size());
        List<Long> missIds = new ArrayList<>();

        for (Long id : ids) {
            String cacheKey = DISH_DETAIL_PREFIX + id;
            String dishJson = redisTemplate.opsForValue().get(cacheKey);
            if (dishJson != null) {
                if ("NULL".equals(dishJson)) {
                    dishMap.put(id, null);
                    continue;
                }
                try {
                    Dish dish = objectMapper.readValue(dishJson, Dish.class);
                    dishMap.put(id, dish);
                } catch (JsonProcessingException e) {
                    log.error("缓存反序列化失败，key: {}", cacheKey, e);
                    redisTemplate.delete(cacheKey);
                    missIds.add(id);
                }
            } else {
                missIds.add(id);
            }
        }

        if (!missIds.isEmpty()) {
            int shardCount = 4;
            List<List<Long>> shards = shardIds(missIds, shardCount);
            for (List<Long> shard : shards) {
                if (shard.isEmpty()) continue;
                String lockKey = DISH_BATCH_LOCK_PREFIX + (shard.get(0) % shardCount);
                try {
                    redisLock.executeWithLock(lockKey, () -> {
                        List<Long> currentMiss = new ArrayList<>();
                        for (Long id : shard) {
                            String cacheKey = DISH_DETAIL_PREFIX + id;
                            String json = redisTemplate.opsForValue().get(cacheKey);
                            if (json == null) {
                                currentMiss.add(id);
                            } else if (!"NULL".equals(json)) {
                                try {
                                    dishMap.put(id, objectMapper.readValue(json, Dish.class));
                                } catch (JsonProcessingException e) {
                                    log.error("双重检查时反序列化失败，key: {}", cacheKey, e);
                                    redisTemplate.delete(cacheKey);
                                    currentMiss.add(id);
                                }
                            }
                        }

                        if (!currentMiss.isEmpty()) {
                            List<Dish> dbDishes = dishMapper.selectByIds(currentMiss);
                            Set<Long> existIds = dbDishes.stream().map(Dish::getId).collect(Collectors.toSet());

                            for (Dish dish : dbDishes) {
                                try {
                                    String cacheKey = DISH_DETAIL_PREFIX + dish.getId();
                                    redisTemplate.opsForValue().set(cacheKey, objectMapper.writeValueAsString(dish), 1, TimeUnit.HOURS);
                                    dishMap.put(dish.getId(), dish);
                                } catch (JsonProcessingException e) {
                                    log.error("缓存写入失败，菜品ID: {}", dish.getId(), e);
                                }
                            }

                            for (Long id : currentMiss) {
                                if (!existIds.contains(id)) {
                                    String cacheKey = DISH_DETAIL_PREFIX + id;
                                    redisTemplate.opsForValue().set(cacheKey, "NULL", 5, TimeUnit.MINUTES);
                                    dishMap.put(id, null);
                                }
                            }
                        }
                        return null;
                    });
                } catch (Exception e) {
                    log.error("批量查询菜品失败，分片ID: {}", shard, e);
                }
            }
        }

        List<Dish> result = new ArrayList<>();
        for (Long id : ids) {
            Dish dish = dishMap.get(id);
            if (dish != null) {
                result.add(dish);
            }
        }
        return result;
    }

    private List<List<Long>> shardIds(List<Long> ids, int shardCount) {
        List<List<Long>> shards = new ArrayList<>(shardCount);
        for (int i = 0; i < shardCount; i++) {
            shards.add(new ArrayList<>());
        }
        for (Long id : ids) {
            int shardIndex = (int) (id % shardCount);
            shards.get(shardIndex).add(id);
        }
        return shards;
    }

    private Long getTotalCount() {
        try {
            String countStr = redisTemplate.opsForValue().get(TOTAL_COUNT_KEY);
            if (countStr != null) {
                return Long.parseLong(countStr);
            }

            return redisLock.executeWithLock(TOTAL_COUNT_KEY, () -> {
                Long totalDP = dishMapper.selectTotalCount();
                redisTemplate.opsForValue().set(TOTAL_COUNT_KEY, totalDP.toString(), 1, TimeUnit.HOURS);
                return totalDP;
            });
        } catch (Exception e) {
            log.error("获取菜品总数量失败", e);
            return 0L;
        }
    }

    private Dish getRandomUniqueDishNotInCategory(List<Dish> dishes, String currentCategory, Set<Long> addedIds) {
        List<Dish> candidates = dishes.stream()
                .filter(d -> !d.getId().toString().equals(currentCategory))
                .filter(d -> !addedIds.contains(d.getId()))
                .toList();
        return candidates.isEmpty() ? null : candidates.get(random.nextInt(candidates.size()));
    }

    private List<Long> getRecommendIdsFromRedis(String key) {
        try {
            String json = redisTemplate.opsForValue().get(key);
            if (json == null) return null;
            Long[] ids = objectMapper.readValue(json, Long[].class);
            return Arrays.asList(ids);
        } catch (Exception e) {
            log.error("从Redis获取推荐列表失败，key: {}", key, e);
            redisTemplate.delete(key);
            return null;
        }
    }

    private void saveRecommendIdsToRedis(String key, List<Long> ids) {
        try {
            redisTemplate.opsForValue().set(key, objectMapper.writeValueAsString(ids), 30, TimeUnit.MINUTES);
        } catch (Exception e) {
            log.error("保存推荐列表到Redis失败，key: {}", key, e);
            throw new RuntimeException("保存推荐序列失败", e);
        }
    }

    private boolean isInvalidParams(Integer category, int count) {
        return category == null || count <= 0;
    }

    private List<String> getRandomDishIds(Integer category, int count) {
        String categoryKey = CATEGORY_IDS_KEY_PREFIX + category;
        if (isCategoryEmpty(categoryKey)) {
            return Collections.emptyList();
        }

        List<String> randomIds = new ArrayList<>();
        try {
            ensureSetType(categoryKey);
            randomIds = redisTemplate.opsForSet().randomMembers(categoryKey, count);
        } catch (Exception e) {
            log.error("从缓存获取随机菜品ID失败，分类: {}", category, e);
            redisTemplate.delete(categoryKey);
        }

        if (isCacheInsufficient(randomIds, count)) {
            randomIds = loadAndCacheCategoryData(category, categoryKey, count);
        }
        return randomIds != null ? randomIds : Collections.emptyList();
    }

    private boolean isCategoryEmpty(String categoryKey) {
        if (!Boolean.TRUE.equals(redisTemplate.hasKey(categoryKey))) {
            return false;
        }
        Long size = redisTemplate.opsForSet().size(categoryKey);
        return size == null || size == 0;
    }

    private boolean isCacheInsufficient(List<String> randomIds, int requiredCount) {
        return randomIds == null || randomIds.size() < requiredCount;
    }

    private List<String> loadAndCacheCategoryData(Integer category, String categoryKey, int count) {
        Lock lock = categoryLocks.computeIfAbsent(category, k -> new ReentrantLock());
        lock.lock();
        try {
            List<String> randomIds = redisTemplate.opsForSet().randomMembers(categoryKey, count);
            if (!isCacheInsufficient(randomIds, count)) {
                return randomIds;
            }

            List<Dish> dishes = getAllDishesWithCache();
            if (dishes.isEmpty()) {
                cacheEmptyCategory(categoryKey);
                return Collections.emptyList();
            }

            List<String> dishIds = cacheDishData(category, dishes);
            redisTemplate.opsForSet().add(categoryKey, dishIds.toArray(new String[0]));
            redisTemplate.expire(categoryKey, NORMAL_CACHE_TTL_SECONDS, TimeUnit.SECONDS);

            return redisTemplate.opsForSet().randomMembers(categoryKey, count);
        } catch (Exception e) {
            log.error("加载并缓存分类数据失败，分类: {}", category, e);
            return Collections.emptyList();
        } finally {
            lock.unlock();
        }
    }

    private void cacheEmptyCategory(String categoryKey) {
        redisTemplate.opsForSet().add(categoryKey);
        redisTemplate.expire(categoryKey, NORMAL_CACHE_TTL_SECONDS, TimeUnit.SECONDS);
    }

    private List<String> cacheDishData(Integer category, List<Dish> dishes) {
        List<String> dishIds = new ArrayList<>();
        Long categoryId = Long.valueOf(category);
        for (Dish dish : dishes) {
            boolean add = false;
            int number = dish.getDishCategoryId();
            while (number > 0) {
                int dishCategory = number % 10;
                if (dishCategory == categoryId) {
                    add = true;
                    break;
                }
                number /= 10;
            }
            if (add) {
                dishIds.add(dish.getId().toString());
            }
        }
        return dishIds;
    }

    private void ensureSetType(String key) {
        DataType type = (DataType) redisTemplate.execute((RedisCallback<Object>) connection ->
                connection.type(Objects.requireNonNull(redisTemplate.getStringSerializer().serialize(key)))
        );

        if (type != DataType.SET) {
            redisTemplate.delete(key);
            redisTemplate.opsForSet().add(key, "init");
            redisTemplate.delete(key);
            redisTemplate.opsForSet().remove(key, "init");
        }
    }
}